﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;

namespace JasonTools
{
    public partial class Calendar : UserControl
    {
        #region Properties

        PhoneApplicationPage _Page = null;
        Brush CellForegroundBrush = new SolidColorBrush(Colors.Black); //(Brush)Application.Current.Resources["PhoneForegroundBrush"];
        Brush CellPhoneBackgroundBrush = new SolidColorBrush(Colors.White);//(Brush)Application.Current.Resources["PhoneBackgroundBrush"];
        Brush HighlightBrush = (Brush)Application.Current.Resources["PhoneAccentBrush"];
        //Brush SelectionHighlightBrush = new SolidColorBrush(Colors.Red);
        Dictionary<DateTime, Rectangle> _Days = new Dictionary<DateTime, Rectangle>();
        public List<DateTime> TaggedDates = new List<DateTime>();
        Dictionary<DateTime, Rectangle> _Tags = new Dictionary<DateTime, Rectangle>();

        #endregion

        #region Constructor

        public Calendar()
        {
            InitializeComponent();
            InitWeekdayTitle();
            CurrentMonth = GetMonth(DateTime.Today);// new DateTime(DateTime.Today.Year, DateTime.Today.Month, 1);
        }

        public Calendar(PhoneApplicationPage page)
            : this()
        {
            _Page = page;
        }

        private bool _KeepExpanded = false;
        public Calendar(PhoneApplicationPage page, bool keepExpanded)
            : this(page)
        {
            _KeepExpanded = keepExpanded;
            if (_KeepExpanded)
            {
                VisualStateManager.GoToState(this, "CalendarShown", false);
            }
        }

        #endregion

        #region Initialization

        private void InitWeekdayTitle()
        {
            this.sundayTitle.DataContext = new DateTime(2011, 5, 1);
            this.mondayTitle.DataContext = new DateTime(2011, 5, 2);
            this.tuesdayTitle.DataContext = new DateTime(2011, 5, 3);
            this.wednesdayTitle.DataContext = new DateTime(2011, 5, 4);
            this.thursdayTitle.DataContext = new DateTime(2011, 5, 5);
            this.fridayTitle.DataContext = new DateTime(2011, 5, 6);
            this.saturdayTitle.DataContext = new DateTime(2011, 5, 7);
        }

        public void SetCalendar()
        {
            DateTime beginOfMonth = CurrentMonth;
            DateTime endOfMonth = beginOfMonth.AddMonths(1).AddDays(-1);

            DateTime beginOfCalendar = beginOfMonth;
            //prefix
            while (beginOfCalendar.DayOfWeek != DayOfWeek.Sunday)
            {
                beginOfCalendar = beginOfCalendar.AddDays(-1);
            }

            DateTime endOfCalenar = endOfMonth;
            //postfix
            while (endOfCalenar.DayOfWeek != DayOfWeek.Saturday)
            {
                endOfCalenar = endOfCalenar.AddDays(+1);
            }

            //populate dates
            matrixGrid.Children.Clear();
            _Days.Clear();
            _Tags.Clear();

            DateTime dt = beginOfCalendar;
            int columnIndex = 0;
            int rowIndex = 0;
            while (dt <= endOfCalenar)
            {
                Rectangle rect = new Rectangle() { Width = 64, Height = 64, Stroke = CellForegroundBrush, Tag = dt };
                if (dt == DateTime.Today)
                {
                    rect.Fill = HighlightBrush;
                    rect.Opacity = 0.5f;
                }
                else
                {
                    rect.Fill = CellPhoneBackgroundBrush;
                }

                rect.MouseLeftButtonDown += new MouseButtonEventHandler(date_MouseLeftButtonDown);
                rect.MouseLeftButtonUp += new MouseButtonEventHandler(date_MouseLeftButtonUp);
                rect.MouseLeave += new MouseEventHandler(rect_MouseLeave);
                matrixGrid.Children.Add(rect);
                rect.SetValue(Grid.ColumnProperty, columnIndex);
                rect.SetValue(Grid.RowProperty, rowIndex);
                _Days.Add(dt, rect);

                if (dt == SelectedDate)
                {
                    HighlightSelectedDateRect(rect);
                }

                TextBlock tb = new TextBlock() { Foreground = CellForegroundBrush };
                tb.Text = dt.Day.ToString();
                if (dt < beginOfMonth || dt > endOfMonth)
                {
                    tb.Opacity = 0.3;
                }

                matrixGrid.Children.Add(tb);
                tb.SetValue(Grid.ColumnProperty, columnIndex);
                tb.SetValue(Grid.RowProperty, rowIndex);
                tb.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
                tb.VerticalAlignment = System.Windows.VerticalAlignment.Bottom;
                tb.Margin = new Thickness(4);

                columnIndex++;
                if (dt.DayOfWeek == DayOfWeek.Saturday)
                {
                    rowIndex++;
                    columnIndex = 0;
                }
                dt = dt.AddDays(1);
            }

            //set tags
            foreach (var date in TaggedDates)
            {
                HighlightTaggedDate(date);
            }

        }

        #endregion

        #region Gesture

        void date_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            Rectangle rect = (Rectangle)sender;
            rect.Fill = CellForegroundBrush;
        }

        void date_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            Rectangle rect = (Rectangle)sender;
            if ((DateTime)rect.Tag == DateTime.Today)
            {
                rect.Fill = HighlightBrush;
            }
            else
            {
                rect.Fill = CellPhoneBackgroundBrush;
            }

            SelectedDate = (DateTime)rect.Tag;

            if (!_KeepExpanded)
            {
                Hide();
            }

            if (DateSelected != null)
            {
                DateSelected(this, SelectedDate);
            }
        }

        void rect_MouseLeave(object sender, MouseEventArgs e)
        {
            Rectangle rect = (Rectangle)sender;
            if ((DateTime)rect.Tag == DateTime.Today)
            {
                rect.Fill = HighlightBrush;
            }
            else
            {
                rect.Fill = CellPhoneBackgroundBrush;
            }
        }

        private void Shadow_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            this.CloseMeAsPopup();
        }

        #endregion

        #region Selected Date

        Rectangle _HighlightRect = null;
        private void HighlightSelectedDateRect(Rectangle rect)
        {
            if (rect == _HighlightRect)
            {
                return;
            }
            
            //resume previous rect
            if (_HighlightRect != null)
            {
                _HighlightRect.Stroke = CellForegroundBrush;
                _HighlightRect.StrokeThickness = 1;
            }

            //set new rect
            rect.Stroke = HighlightBrush;// SelectionHighlightBrush;
            rect.StrokeThickness = 2;

            _HighlightRect = rect;
        }

        private void HighlightSelectedDate()
        {
            if (_Days.Keys.Contains(SelectedDate))
            {
                HighlightSelectedDateRect(_Days[SelectedDate]);
            }
        }

        #endregion

        #region Tag Dates

        public void TagDate(DateTime date)
        {
            DateTime normalizedDate = new DateTime(date.Year, date.Month, date.Day);
            if (GetMonth(normalizedDate) == CurrentMonth && !TaggedDates.Contains(normalizedDate))
            {
                TaggedDates.Add(normalizedDate);
                HighlightTaggedDate(normalizedDate);
            }
        }

        private void HighlightTaggedDate(DateTime date)
        {
            if (_Days.Keys.Contains(date))
            {
                var dateRect = _Days[date];
                var columnIndex = (int)dateRect.GetValue(Grid.ColumnProperty);
                var rowIndex = (int)dateRect.GetValue(Grid.RowProperty);
                Rectangle tagRect = GetTagShape();
                matrixGrid.Children.Add(tagRect);
                tagRect.SetValue(Grid.ColumnProperty, columnIndex);
                tagRect.SetValue(Grid.RowProperty, rowIndex);

                _Tags.Add(date, tagRect);
            }
        }

        private Rectangle GetTagShape()
        {
            Rectangle rect = new Rectangle() { Width = 64, Height = 64, Fill = HighlightBrush };
            RectangleGeometry geometry = new RectangleGeometry();
            geometry.Rect = new Rect(26.745, 72, 64, 64);
            CompositeTransform transform = new CompositeTransform();
            transform.CenterX = 26.7452;
            transform.CenterY = 72;
            transform.Rotation = -45;
            geometry.Transform = transform;
            rect.Clip = geometry;

            return rect;
        }

        #endregion

        #region Show & Hide

        public void Show()
        {
            CurrentMonth = GetMonth(SelectedDate);// new DateTime(SelectedDate.Year, SelectedDate.Month, 1);

            if (_PopupCotainer == null)
            {
                _PopupCotainer = new PopupCotainer(_Page);
                //_PopupCotainer.OnClosed += new EventHandler(Popup_Closed);
            }

            _PopupCotainer.Show(this);
        }

        public void Hide()
        {
            this.CloseMeAsPopup();
        }

        #endregion

        #region SelectedDate Property

        public static readonly DependencyProperty SelectedDateProperty =
            DependencyProperty.Register(
                "SelectedDate",
                typeof(DateTime),
                typeof(Calendar),
                new PropertyMetadata(DateTime.Today, SelectedDatePropertyChanged));

        private static void SelectedDatePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            Calendar calendar = d as Calendar;
            var month = GetMonth((DateTime)e.NewValue);// new DateTime(((DateTime)e.NewValue).Year, ((DateTime)e.NewValue).Month, 1);
            if (calendar.CurrentMonth == month)
            {
                calendar.HighlightSelectedDate();
            }
            else
            {
                calendar.CurrentMonth = month;
            }

        }

        public DateTime SelectedDate
        {
            get { return (DateTime)GetValue(SelectedDateProperty); }
            set
            {
                DateTime trimedDate = new DateTime(value.Year, value.Month, value.Day);
                SetValue(SelectedDateProperty, trimedDate);
            }
        }

        public delegate void DateSelectedEventHandler(object sender, DateTime e);
        public event DateSelectedEventHandler DateSelected;

        #endregion

        #region CurrentMonth

        private DateTime _CurrentMonth = DateTime.MinValue;
        private DateTime CurrentMonth
        {
            get
            {
                return _CurrentMonth;
            }
            set
            {
                if (_CurrentMonth != value)
                {
                    _CurrentMonth = value;
                    SetCalendar();
                    if (monthSlider.SelectedMonth != value)
                    {
                        monthSlider.SelectedMonth = value;
                    }
                }
            }
        }

        #endregion

        #region MonthSlider

        private void MonthSlider_SelectedMonthChanged(object sender, DateTime e)
        {
            CurrentMonth = e;
        }

        #endregion

        #region Popup

        PopupCotainer _PopupCotainer = null;

        //void Popup_Closed(object sender, EventArgs e)
        //{

        //}

        #endregion

        #region DateTime Handling

        private static DateTime GetMonth(DateTime date)
        {
            return new DateTime(date.Year, date.Month, 1);
        }

        #endregion

    }
}
